#include "lua_base.hpp"
#include "data/editor_data.hpp"
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"

#include "scripting/extra/info_api.hpp"
#include "sql/types.hpp"
#include "sql/sql_data_extensions.hpp"
#include "utils/app_utils.hpp"
#include "utils/date_utils.hpp"
#include "utils/log.hpp"
#include "utils/path_utils.hpp"
#include "utils/shell_utils.hpp"

#include <LuaBridge/LuaBridge.h>
#include <LuaBridge/Vector.h>
#include <LuaBridge/List.h>

struct System {
    static void CopyText(std::string text)
    {
        ::CopyText(text);
    }

    static void I(std::string msg) { SendInfo(msg); }
    static void W(std::string msg) { SendWarning(msg); }
    static void E(std::string msg) { SendError(msg); }
    static void P(bool show) { ShowProgressDialog(show); }
    static void N(std::string msg, double ms) { SendNotification(msg, ms); }

    static void LogD(std::string msg) { log_printf("<script>", 1, LOG_LEVEL_DEBUG, "%s", msg.c_str()); }
    static void LogI(std::string msg) { log_printf("<script>", 1, LOG_LEVEL_INFO, "%s", msg.c_str()); }
    static void LogW(std::string msg) { log_printf("<script>", 1, LOG_LEVEL_WARN, "%s", msg.c_str()); }
    static void LogE(std::string msg) { log_printf("<script>", 1, LOG_LEVEL_ERROR, "%s", msg.c_str()); }
};

static void RegisterTypes(lua_State *L) {
    using namespace luabridge;
    getGlobalNamespace(L)
        // log
        .addFunction("log_d", &System::LogD)
        .addFunction("log_i", &System::LogI)
        .addFunction("log_w", &System::LogW)
        .addFunction("log_e", &System::LogE)
        // dialog
        .addFunction("show_info", &System::I)
        .addFunction("show_warning", &System::W)
        .addFunction("show_error", &System::E)
        .addFunction("show_progress", &System::P)
        // notify
        .addFunction("notify", &System::N)
        // clipboard
        .addFunction("copy_text", &System::CopyText)
        // version
        .addFunction("app_version_str", &GetAppVersionText)
        .addFunction("app_version_info", &GetAppVersionInfo)
        .addFunction("app_version_code", &GetAppVersionCode)
        .addFunction("data_version_str", &GetDataVersion)
        .addFunction("data_version_code", &GetDataVersionCode)
        // path
        .addFunction("temp_path", &GetTempFilePath)
        .addFunction("asset_path", &GetAssetsPath)
        .addFunction("data_path", &GetDataPath)
        // shell utils
        .addFunction("choose_file", &ChooseFile)
        .addFunction("choose_files", &ChooseFiles)
        .addFunction("save_file", &SaveFile)
        .addFunction("pick_folder", &PickFolder)
        .addFunction("show_logs", &ShowLogFileContent)
        // date utils
        .addFunction("yestoday", &GetYesterday)
        .addFunction("today", &GetToday)
        .addFunction("tommorrow", &GetTomorrow)
        // app utils
        .addFunction("create_work", &CreateWorkItem)
        .addFunction("create_work_with_note", &CreateWorkItemWithNote)
        .addFunction("show_statistics", &ShowStatistics)
        // data ext
        .addFunction("get_issue_project_desc", sql_data_ext.get_issue_project_desc)
        ;

    getGlobalNamespace(L)
        .beginClass<Tag>("Tag")
            .addProperty("name", &Tag::tag_name, false)
            .addProperty("level", &Tag::tag_level, false)
            .addProperty("color", &Tag::tag_color, false)
        .endClass()
        .beginClass<WorkItem>("WorkItem")
            .addProperty("create_date", &WorkItem::create_date, false)
            .addProperty("hour", &WorkItem::hour, false)
            .addProperty("comment", &WorkItem::comment, false)
            .addProperty("note", &WorkItem::note, false)
            .addProperty("act", &WorkItem::act_id, false)
            .addProperty("issue", &WorkItem::issue_id, false)
        .endClass()
        .beginClass<ItemNode>("ItemNode")
            .addProperty("item", &ItemNode::item)
            .addProperty("tags", &ItemNode::item_tags)
        .endClass()
        .beginClass<DayLevelNode>("DayLevelNode")
            .addProperty("title", &DayLevelNode::title, false)
            .addProperty("key", &DayLevelNode::child_key, false)
            .addProperty("children", &DayLevelNode::children, false)
            .addFunction("load", &DayLevelNode::LoadInnerData)
        .endClass()
        .beginClass<MonthLevelNode>("MonthLevelNode")
            .addProperty("title", &MonthLevelNode::title, false)
            .addProperty("key", &MonthLevelNode::child_key, false)
            .addProperty("children", &MonthLevelNode::children, false)
            .addFunction("load", &MonthLevelNode::LoadInnerData)
        .endClass()
        .beginClass<YearLevelNode>("YearLevelNode")
            .addProperty("title", &YearLevelNode::title, false)
            .addProperty("key", &YearLevelNode::child_key, false)
            .addProperty("children", &YearLevelNode::children, false)
            .addFunction("load", &YearLevelNode::LoadInnerData)
        .endClass();
}

lua_State* LuaShared::NewState()
{
    return lua_newthread(Instance().global_);
}

int LuaShared::CloseState(lua_State *state)
{
    return lua_closethread(state, Instance().global_);
}

LuaShared::LuaShared(): global_(nullptr)
{
    global_ = luaL_newstate();
    luaL_openlibs(global_);
    RegisterTypes(global_);
    lua_setwarnf(global_, [](void*,const char *msg, int tocon){
        L_WARN("Lua warning: %s, %d", msg, tocon);
    }, nullptr);
}

LuaShared::~LuaShared()
{
    lua_close(global_);
    global_ = nullptr;
}

LuaScriptBase::LuaScriptBase()
{
    l_ = LuaShared::NewState();
}

LuaScriptBase::~LuaScriptBase()
{
    LuaShared::CloseState(l_);
}


